/*
 *  Copyright (C) 2009  Peter Kist & Jan Ripke
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <boost/foreach.hpp>

#include "Galaxy.h"
#include "Consumer.h"
#include "AppContext.h"

#include <core/Logger.h>
#include <galaxy/ManagerFactory.h>
#include <input/MediatorTypes.h>
#include <logic/Interface.h>
#include <mediator/Interface.h>
#include <mediator/InterMediator.h>

namespace galaxy {

    Galaxy::Galaxy()
    {
    }

    Galaxy::~Galaxy()
    {
    }

    void Galaxy::run () {
        initialize();

        /* initialize the 'threaded' modules */
        if (initializeModules() == false) {
            LOG_ERROR ("initializeModules failed");
            return;
        }

        loadInitialGameData();
        setInitialGameState();

        /** wait for game to signal that it is ended.
        * The input module will post all keyboard event to the galaxy.consumer object. This consumer will
        * notify all threads to quit. After all thread are quit this thread will continue (and quit the game)
        * @see signalTerminateGame
        */
        m_Threads.join_all();

        terminateModules();
    }

    /** initialize the galaxy game object.
    * @return true upon success, false if not.
    */
    bool Galaxy::initialize () {
        Consumer * consumer = new Consumer(*this);
        m_Consumer.reset (consumer);

        // register the galaxy consumer with the mediator. It is interested in action EXIT, which is
        // send by the input module (key-release).
        mediator::InterMediator::registerConsumer(*consumer);
        mediator::InterMediator::registerInterest (consumer->consumerType(), 
            mediator::Identifier (mediator::CATEGORY_INPUT, MODULE_TYPE_INPUT_KEY, MODULE_SUBTYPE_INPUT_KEYRELEASE));

        return true;
    }

    /** Initialize the modules used by the game. 
    * @return true upon success, false if not.
    */
    bool Galaxy::initializeModules() {

        LOG_TRACE ("Initializing game modules");

        // create the managers
        ManagerFactory::getManager(mediator::CATEGORY_GFX);
        ManagerFactory::getManager(mediator::CATEGORY_INPUT);
        ManagerFactory::getManager(mediator::CATEGORY_LOGIC);

        ManagerFactory::ManagerMap& managers = ManagerFactory::getManagerMap ();
        
        BOOST_FOREACH (ManagerFactory::ManagerMap::value_type i, managers) {
            LOG_TRACE (std::string ("initializing game module ") + i.second->moduleName ());
            if (i.second->initialize() == false) {
                LOG_ERROR (std::string ("error initializing manager ") + i.second->moduleName());
                return false;
            }
        }
        BOOST_FOREACH (ManagerFactory::ManagerMap::value_type i, managers) {
            LOG_TRACE (std::string ("post initialization of game module ") + i.second->moduleName ());
            if (i.second->postInitialize() == false) {
                LOG_ERROR (std::string ("error during post initialize of module ") + i.second->moduleName());
                return false;
            }
        }
        BOOST_FOREACH (ManagerFactory::ManagerMap::value_type i, managers) {
            boost::thread * thrd = i.second->startManagerThread ();
            if (thrd) {
                LOG_TRACE (std::string ("starting thread for module ") + i.second->moduleName());
                m_Threads.add_thread(thrd);
            }
        }
     
        LOG_TRACE ("game modules initialized");

        return true;
    }

    bool Galaxy::loadInitialGameData () {
        return true;
    }

    /** Setup initial game state. Notify logic (gamestate) module that the main state 
    * is to be initialized.
    */
    bool Galaxy::setInitialGameState() {
        mediator::RequestResultPtr RequestResult;
        logic::StateRequest Request (MODULE_TYPE_LOGIC_SET_STATE, logic::State::c_MainMenu);

        mediator::InterMediator::RequestProducer(Request, RequestResult);
        return false;
    }

    /** unblocks the game loop so that it can be terminated.
    * It unblocks by terminating all running 'manager' threads.
    */
    void Galaxy::signalTerminateGame () {
        LOG_TRACE ("signalling game termination to game modules");
        ManagerFactory::ManagerMap& managers = ManagerFactory::getManagerMap ();
        BOOST_FOREACH (ManagerFactory::ManagerMap::value_type i, managers) {
            LOG_TRACE (std::string ("requesting module ") + i.second->moduleName() + " to terminate its thread");
            i.second->terminateThread();
        }
    }


    bool Galaxy::terminateModules () {
        ManagerFactory::ManagerMap& managers = ManagerFactory::getManagerMap ();

        BOOST_FOREACH (ManagerFactory::ManagerMap::value_type i, managers) {
            LOG_TRACE (std::string ("pre destroy game module ") + i.second->moduleName ());
            i.second->preDestroy();
        }
        BOOST_FOREACH (ManagerFactory::ManagerMap::value_type i, managers) {
            LOG_TRACE (std::string ("destroying game module ") + i.second->moduleName ());
            i.second->destroy();
        }

        return true;
    }

} // namespace galaxy
